#include "hashset.h"
#include "linkedlist.c"
#include "Math.h"
#include "stdlib.h"

/*
 * Structures definitions
*/

struct hpair_adt {
	TypeKey key;
	void * value;
};

struct hash_adt {
	long int capacity;
	char h_function;
	LinkedList * h;
};

/**
 @brief H_DFmod this is used to represent function h(x) = x mod n, where n is the linear dimension of the HashSet
*/

const char H_DFmod = 1;

/**
 @brief H_DFmul this is used to represent function h(x) = [n(Ax - [Ax])], where A is a number such as 0 < A < 1 and n is the linear dimension of 
 the HashSet. This implementation uses A = (sqrt(5) - 1)/2 
*/

const double H_A = 0.6180339887;
const char H_DFmul = 2;

/*
 * private functions
*/

TypeKey H_hash(HashSet h, TypeKey n){
	char tf = h->h_function;
	switch(tf){
		case H_DFmod : return (TypeKey)(n % h->capacity);
		case H_DFmul : return (TypeKey)(floor(h->capacity * (H_A * n - floor(H_A * n))));
		default : return (TypeKey)(n % h->capacity);
	}
}

long int H_choose(long int n){
	if(n < 10) return 7;
	else if(n < 100) return 71;
	else if(n < 1000) return 123;
	else if(n < 10000) return 1031;
	else if(n < 100000) return 11231;
	else if(n < 1000000) return 171233;
	else if(n < 10000000) return 987551;
	else return 1123551;
}
/*
 * Public functions
*/ 

HashSet H_new(){
	return H_new(H_DFmod, 971);
}

HashSet H_new(char f_type){
	return H_new(f_type, 971);
}

HashSet H_new(long int n){
	return H_new(H_DFmod, n);
}

HashSet H_new(char f_type, long int n){
	int i;
	HashSet hs = (HashSet)malloc(sizeof(struct hash_adt));
	hs->capacity = H_choose(n);
	hs->h_function = f_type;
	hs->h = (LinkedList *)malloc(sizeof(LinkedList) * n);
	for(i = 0;i < n;i++)
		hs->h[i] = LL_New();
	return hs;
}

void H_Free(HashSet h){
	int i;
	if(h != NULL){
		for(i = 0;i < h->capacity;i++)
			LL_Free(h->h[i]);
		free(h->h);
		free(h);
	}
}

void H_Add(HashSet h, TypeKey key, void * value){
	TypeKey kd = H_hash(h, key);
	HashPair pr = (HashPair)malloc(sizeof(struct hpair_adt));
	pr->key = key;
	pr->value = value;
	LL_Add(h->h[kd], LL_End(h->h[kd]), (void*)pr);
}

void H_Del(HashSet h, TypeKey key){
	TypeKey kd = H_hash(h, key);
	LinkedList lt = h->h[kd];
	LinkedListIterator it = LL_Begin(lt);
	HashPair hp = (HashPair)malloc(sizeof(struct hpair_adt));
	while(it != LL_End(lt)){
		hp = (HashPair)LL_Get(lt, it);
		if(hp->key == key){
			LL_Remove(lt, it);
			return;
		}
		it = LL_Next(lt, it);
	}
}

int H_Find(HashSet h, TypeKey key){
	TypeKey kd = H_hash(h, key);
	LinkedList lt = h->h[kd];
	LinkedListIterator it = LL_Begin(lt);
	HashPair hp = (HashPair)malloc(sizeof(struct hpair_adt));
	while(it != LL_End(lt)){
		hp = (HashPair)LL_Get(lt, it);
		if(hp->key == key) 
			return 1;
		it = LL_Next(lt, it);
	}
	return 0;
}

void * H_Get(HashSet h, TypeKey key){
	TypeKey kd = H_hash(h, key);
	LinkedList lt = h->h[kd];
	LinkedListIterator it = LL_Begin(lt);
	HashPair hp = (HashPair)malloc(sizeof(struct hpair_adt));
	while(it != LL_End(lt)){
		hp = (HashPair)LL_Get(lt, it);
		if(hp->key == key) 
			return hp->value;
		it = LL_Next(lt, it);
	}
	return NULL;
}

void H_Set(HashSet h, TypeKey key, void * nvalue){
	H_Del(h, key);
	H_Add(h, key, nvalue);
}
